<template>
	<xInput
		class="el-date-editor"
		:class="cptClassDatePickerInput"
		:readonly="cptIsReadonly"
		:disabled="pickerDisabled"
		:size="pickerSize"
		:name="name"
		v-bind="firstInputId"
		v-if="!ranged"
		v-clickoutside="handleClose"
		:placeholder="placeholder"
		@focus="handleFocus"
		@keydown.native="handleKeydown"
		:value="displayValue"
		@input="value => (userInput = value)"
		@change="handleChange"
		@mouseenter.native="handleMouseEnter"
		@mouseleave.native="showClose = false"
		:validateEvent="false"
		ref="reference">
		<i slot="prefix" class="el-input__icon" :class="triggerClass" @click="handleFocus"> </i>
		<i
			slot="suffix"
			class="el-input__icon"
			@click="handleClickIcon"
			:class="[showClose ? '' + clearIcon : '']"
			v-if="haveTrigger">
		</i>
	</xInput>
	<div
		class="el-date-editor el-range-editor el-input__inner"
		:class="cptClassDatePickerInputInner"
		@click="handleRangeClick"
		@mouseenter="handleMouseEnter"
		@mouseleave="showClose = false"
		@keydown="handleKeydown"
		ref="reference"
		v-clickoutside="handleClose"
		v-else>
		<i :class="['el-input__icon', 'el-range__icon', triggerClass]"></i>
		<input
			autocomplete="off"
			:placeholder="startPlaceholder"
			:value="displayValue && displayValue[0]"
			:disabled="pickerDisabled"
			v-bind="firstInputId"
			:readonly="!editable || readonly"
			:name="name && name[0]"
			@input="handleStartInput"
			@change="handleStartChange"
			@focus="handleFocus"
			class="el-range-input" />
		<slot name="range-separator">
			<span class="el-range-separator">{{ rangeSeparator }}</span>
		</slot>
		<input
			autocomplete="off"
			:placeholder="endPlaceholder"
			:value="displayValue && displayValue[1]"
			:disabled="pickerDisabled"
			v-bind="secondInputId"
			:readonly="!editable || readonly"
			:name="name && name[1]"
			@input="handleEndInput"
			@change="handleEndChange"
			@focus="handleFocus"
			class="el-range-input" />
		<i
			@click="handleClickIcon"
			v-if="haveTrigger"
			:class="[showClose ? '' + clearIcon : '']"
			class="el-input__icon el-range__close-icon">
		</i>
	</div>
</template>
<script lang="ts">
export default async function ({ PRIVATE_GLOBAL }) {
	const [Popper, { formatDate, parseDate, isDateObject, getWeekNumber }, Clickoutside] =
		await _.$importVue([
			"/common/libs/VuePopper/VuePopper.vue",
			"/common/ui-x/components/form/xDatePicker/dateUtils.vue",
			"/common/ui-x/directive/clickoutside.vue"
		]);

	const NewPopper = {
		props: {
			appendToBody: Popper.props.appendToBody,
			offset: Popper.props.offset,
			boundariesPadding: Popper.props.boundariesPadding,
			arrowOffset: Popper.props.arrowOffset,
			transformOrigin: Popper.props.transformOrigin
		},
		methods: Popper.methods,
		data() {
			return _.merge({ visibleArrow: true }, Popper.data);
		},
		beforeDestroy: Popper.beforeDestroy
	};

	const DEFAULT_FORMATS = {
		date: "yyyy-MM-dd",
		month: "yyyy-MM",
		months: "yyyy-MM",
		datetime: "yyyy-MM-dd HH:mm:ss",
		time: "HH:mm:ss",
		week: "yyyywWW",
		timerange: "HH:mm:ss",
		daterange: "yyyy-MM-dd",
		monthrange: "yyyy-MM",
		datetimerange: "yyyy-MM-dd HH:mm:ss",
		year: "yyyy",
		years: "yyyy"
	};
	const HAVE_TRIGGER_TYPES = [
		"date",
		"datetime",
		"time",
		"time-select",
		"week",
		"month",
		"year",
		"daterange",
		"monthrange",
		"timerange",
		"datetimerange",
		"dates",
		"months",
		"years"
	];

	const DATE_FORMATTER = function (value, format) {
		if (format === "timestamp") return value.getTime();
		return formatDate(value, format);
	};
	const DATE_PARSER = function (text, format) {
		if (format === "timestamp") return new Date(Number(text));
		return parseDate(text, format);
	};
	const RANGE_FORMATTER = function (value, format) {
		if (Array.isArray(value) && value.length === 2) {
			const start = value[0];
			const end = value[1];

			if (start && end) {
				return [DATE_FORMATTER(start, format), DATE_FORMATTER(end, format)];
			}
		}
		return "";
	};
	const RANGE_PARSER = function (array, format, separator) {
		if (!Array.isArray(array)) {
			array = array.split(separator);
		}
		if (array.length === 2) {
			const range1 = array[0];
			const range2 = array[1];

			return [DATE_PARSER(range1, format), DATE_PARSER(range2, format)];
		}
		return [];
	};
	const TYPE_VALUE_RESOLVER_MAP = {
		default: {
			formatter(value) {
				if (!value) return "";
				return "" + value;
			},
			parser(text) {
				if (text === undefined || text === "") return null;
				return text;
			}
		},
		week: {
			formatter(value, format) {
				let week = getWeekNumber(value);
				let month = value.getMonth();
				const trueDate = new Date(value);
				if (week === 1 && month === 11) {
					trueDate.setHours(0, 0, 0, 0);
					trueDate.setDate(trueDate.getDate() + 3 - ((trueDate.getDay() + 6) % 7));
				}
				let date = formatDate(trueDate, format);

				date = /WW/.test(date)
					? date.replace(/WW/, week < 10 ? "0" + week : week)
					: date.replace(/W/, week);
				return date;
			},
			parser(text, format) {
				// parse as if a normal date
				return TYPE_VALUE_RESOLVER_MAP.date.parser(text, format);
			}
		},
		date: {
			formatter: DATE_FORMATTER,
			parser: DATE_PARSER
		},
		datetime: {
			formatter: DATE_FORMATTER,
			parser: DATE_PARSER
		},
		daterange: {
			formatter: RANGE_FORMATTER,
			parser: RANGE_PARSER
		},
		monthrange: {
			formatter: RANGE_FORMATTER,
			parser: RANGE_PARSER
		},
		datetimerange: {
			formatter: RANGE_FORMATTER,
			parser: RANGE_PARSER
		},
		timerange: {
			formatter: RANGE_FORMATTER,
			parser: RANGE_PARSER
		},
		time: {
			formatter: DATE_FORMATTER,
			parser: DATE_PARSER
		},
		month: {
			formatter: DATE_FORMATTER,
			parser: DATE_PARSER
		},
		year: {
			formatter: DATE_FORMATTER,
			parser: DATE_PARSER
		},
		number: {
			formatter(value) {
				if (!value) return "";
				return "" + value;
			},
			parser(text) {
				let result = Number(text);

				if (!isNaN(text)) {
					return result;
				} else {
					return null;
				}
			}
		},
		dates: {
			formatter(value, format) {
				return value.map(date => DATE_FORMATTER(date, format));
			},
			parser(value, format) {
				return (typeof value === "string" ? value.split(", ") : value).map(date =>
					date instanceof Date ? date : DATE_PARSER(date, format)
				);
			}
		},
		months: {
			formatter(value, format) {
				return value.map(date => DATE_FORMATTER(date, format));
			},
			parser(value, format) {
				return (typeof value === "string" ? value.split(", ") : value).map(date =>
					date instanceof Date ? date : DATE_PARSER(date, format)
				);
			}
		},
		years: {
			formatter(value, format) {
				return value.map(date => DATE_FORMATTER(date, format));
			},
			parser(value, format) {
				return (typeof value === "string" ? value.split(", ") : value).map(date =>
					date instanceof Date ? date : DATE_PARSER(date, format)
				);
			}
		}
	};
	const PLACEMENT_MAP = {
		left: "bottom-start",
		center: "bottom",
		right: "bottom-end"
	};

	const parseAsFormatAndType = (value, customFormat, type, rangeSeparator = "-") => {
		if (!value) return null;
		const parser = (TYPE_VALUE_RESOLVER_MAP[type] || TYPE_VALUE_RESOLVER_MAP["default"]).parser;
		const format = customFormat || DEFAULT_FORMATS[type];
		return parser(value, format, rangeSeparator);
	};

	const formatAsFormatAndType = (value, customFormat, type) => {
		if (!value) return null;
		const formatter = (TYPE_VALUE_RESOLVER_MAP[type] || TYPE_VALUE_RESOLVER_MAP["default"])
			.formatter;
		const format = customFormat || DEFAULT_FORMATS[type];
		return formatter(value, format);
	};

	/*
	 * Considers:
	 *   1. Date object
	 *   2. date string
	 *   3. array of 1 or 2
	 */
	const valueEquals = function (a, b) {
		// considers Date object and string
		const dateEquals = function (a, b) {
			const aIsDate = a instanceof Date;
			const bIsDate = b instanceof Date;
			if (aIsDate && bIsDate) {
				return a.getTime() === b.getTime();
			}
			if (!aIsDate && !bIsDate) {
				return a === b;
			}
			return false;
		};

		const aIsArray = a instanceof Array;
		const bIsArray = b instanceof Array;
		if (aIsArray && bIsArray) {
			if (a.length !== b.length) {
				return false;
			}
			return a.every((item, index) => dateEquals(item, b[index]));
		}
		if (!aIsArray && !bIsArray) {
			return dateEquals(a, b);
		}
		return false;
	};

	const isString = function (val) {
		return typeof val === "string" || val instanceof String;
	};

	const validator = function (val) {
		// either: String, Array of String, null / undefined
		return (
			val === null ||
			val === undefined ||
			isString(val) ||
			(Array.isArray(val) && val.length === 2 && val.every(isString))
		);
	};

	return defineComponent({
		mixins: [NewPopper],
		inject: {
			elForm: {
				default: ""
			},
			elFormItem: {
				default: ""
			}
		},

		props: {
			size: String,
			format: String,
			valueFormat: String,
			readonly: Boolean,
			placeholder: String,
			startPlaceholder: String,
			endPlaceholder: String,
			prefixIcon: String,
			clearIcon: {
				type: String,
				default: "el-icon-circle-close"
			},
			name: {
				default: "",
				validator
			},
			disabled: Boolean,
			clearable: {
				type: Boolean,
				default: true
			},
			id: {
				default: "",
				validator
			},
			popperClass: String,
			editable: {
				type: Boolean,
				default: true
			},
			align: {
				type: String,
				default: "left"
			},
			value: {},
			defaultValue: {},
			defaultTime: {},
			rangeSeparator: {
				default: "-"
			},
			pickerOptions: {},
			unlinkPanels: Boolean,
			validateEvent: {
				type: Boolean,
				default: true
			}
		},

		directives: { Clickoutside },

		data() {
			return {
				pickerVisible: false,
				showClose: false,
				userInput: null,
				valueOnOpen: null, // value when picker opens, used to determine whether to emit change
				unwatchPickerOptions: null
			};
		},

		watch: {
			pickerVisible(val) {
				if (this.readonly || this.pickerDisabled) return;
				if (val) {
					this.showPicker();
					this.valueOnOpen = Array.isArray(this.value) ? [...this.value] : this.value;
				} else {
					this.hidePicker();
					this.emitChange(this.value);
					this.userInput = null;
					if (this.validateEvent) {
						this.dispatch("ElFormItem", "el.form.blur");
					}
					this.$emit("blur", this);
					this.blur();
				}
			},
			parsedValue: {
				immediate: true,
				handler(val) {
					if (this.picker) {
						this.picker.value = val;
					}
				}
			},
			defaultValue(val) {
				// NOTE: should eventually move to jsx style picker + panel ?
				if (this.picker) {
					this.picker.defaultValue = val;
				}
			},
			value(val, oldVal) {
				if (!valueEquals(val, oldVal) && !this.pickerVisible && this.validateEvent) {
					this.dispatch("ElFormItem", "el.form.change", val);
				}
			}
		},

		computed: {
			cptClassDatePickerInputInner() {
				const { type, pickerSize, pickerDisabled, pickerVisible } = this;
				return [
					"el-date-editor--" + type,
					pickerSize ? `el-range-editor--${pickerSize}` : "",
					pickerDisabled ? "is-disabled" : "",
					pickerVisible ? "is-active" : ""
				];
			},
			cptIsReadonly() {
				const { editable, readonly, type } = this;
				return (
					!editable ||
					readonly ||
					type === "dates" ||
					type === "week" ||
					type === "years" ||
					type === "months"
				);
			},
			cptClassDatePickerInput() {
				return `el-date-editor--${this.type}`;
			},
			ranged() {
				return this.type.indexOf("range") > -1;
			},

			reference() {
				const reference = this.$refs.reference;
				return reference.$el || reference;
			},

			refInput() {
				if (this.reference) {
					return [].slice.call(this.reference.querySelectorAll("input"));
				}
				return [];
			},

			valueIsEmpty() {
				const val = this.value;
				if (Array.isArray(val)) {
					for (let i = 0, len = val.length; i < len; i++) {
						if (val[i]) {
							return false;
						}
					}
				} else {
					if (val) {
						return false;
					}
				}
				return true;
			},

			triggerClass() {
				return (
					this.prefixIcon ||
					(this.type.indexOf("time") !== -1 ? "el-icon-time" : "el-icon-date")
				);
			},

			selectionMode() {
				if (this.type === "week") {
					return "week";
				} else if (this.type === "month") {
					return "month";
				} else if (this.type === "year") {
					return "year";
				} else if (this.type === "dates") {
					return "dates";
				} else if (this.type === "months") {
					return "months";
				} else if (this.type === "years") {
					return "years";
				}

				return "day";
			},

			haveTrigger() {
				if (typeof this.showTrigger !== "undefined") {
					return this.showTrigger;
				}
				return HAVE_TRIGGER_TYPES.indexOf(this.type) !== -1;
			},

			displayValue() {
				const formattedValue = formatAsFormatAndType(
					this.parsedValue,
					this.format,
					this.type,
					this.rangeSeparator
				);
				if (Array.isArray(this.userInput)) {
					return [
						this.userInput[0] || (formattedValue && formattedValue[0]) || "",
						this.userInput[1] || (formattedValue && formattedValue[1]) || ""
					];
				} else if (this.userInput !== null) {
					return this.userInput;
				} else if (formattedValue) {
					return this.type === "dates" || this.type === "years" || this.type === "months"
						? formattedValue.join(", ")
						: formattedValue;
				} else {
					return "";
				}
			},

			parsedValue() {
				if (!this.value) return this.value; // component value is not set
				if (this.type === "time-select") return this.value; // time-select does not require parsing, this might change in next major version

				const valueIsDateObject =
					isDateObject(this.value) ||
					(Array.isArray(this.value) && this.value.every(isDateObject));
				if (valueIsDateObject) {
					return this.value;
				}

				if (this.valueFormat) {
					return (
						parseAsFormatAndType(
							this.value,
							this.valueFormat,
							this.type,
							this.rangeSeparator
						) || this.value
					);
				}

				// NOTE: deal with common but incorrect usage, should remove in next major version
				// user might provide string / timestamp without value-format, coerce them into date (or array of date)
				return Array.isArray(this.value)
					? this.value.map(val => new Date(val))
					: new Date(this.value);
			},

			_elFormItemSize() {
				return (this.elFormItem || {}).elFormItemSize;
			},

			pickerSize() {
				return this.size || this._elFormItemSize || PRIVATE_GLOBAL.x_ui.size;
			},

			pickerDisabled() {
				return this.disabled || (this.elForm || {}).disabled;
			},

			firstInputId() {
				const obj = {};
				let id;
				if (this.ranged) {
					id = this.id && this.id[0];
				} else {
					id = this.id;
				}
				if (id) obj.id = id;
				return obj;
			},

			secondInputId() {
				const obj = {};
				let id;
				if (this.ranged) {
					id = this.id && this.id[1];
				}
				if (id) obj.id = id;
				return obj;
			}
		},

		created() {
			// vue-popper
			this.popperOptions = {
				boundariesPadding: 0,
				gpuAcceleration: false
			};
			this.placement = PLACEMENT_MAP[this.align] || PLACEMENT_MAP.left;

			this.$on("fieldReset", this.handleFieldReset);
		},

		methods: {
			focus() {
				if (!this.ranged) {
					this.$refs.reference.focus();
				} else {
					this.handleFocus();
				}
			},

			blur() {
				this.refInput.forEach(input => input.blur());
			},

			// {parse, formatTo} Value deals maps component value with internal Date
			parseValue(value) {
				const isParsed =
					isDateObject(value) || (Array.isArray(value) && value.every(isDateObject));
				if (this.valueFormat && !isParsed) {
					return (
						parseAsFormatAndType(
							value,
							this.valueFormat,
							this.type,
							this.rangeSeparator
						) || value
					);
				} else {
					return value;
				}
			},

			formatToValue(date) {
				const isFormattable =
					isDateObject(date) || (Array.isArray(date) && date.every(isDateObject));
				if (this.valueFormat && isFormattable) {
					return formatAsFormatAndType(
						date,
						this.valueFormat,
						this.type,
						this.rangeSeparator
					);
				} else {
					return date;
				}
			},

			// {parse, formatTo} String deals with user input
			parseString(value) {
				const type = Array.isArray(value) ? this.type : this.type.replace("range", "");
				return parseAsFormatAndType(value, this.format, type);
			},

			formatToString(value) {
				const type = Array.isArray(value) ? this.type : this.type.replace("range", "");
				return formatAsFormatAndType(value, this.format, type);
			},

			handleMouseEnter() {
				if (this.readonly || this.pickerDisabled) return;
				if (!this.valueIsEmpty && this.clearable) {
					this.showClose = true;
				}
			},

			handleChange() {
				if (this.userInput) {
					const value = this.parseString(this.displayValue);
					if (value) {
						this.picker.value = value;
						if (this.isValidValue(value)) {
							this.emitInput(value);
							this.userInput = null;
						}
					}
				}
				if (this.userInput === "") {
					this.emitInput(null);
					this.emitChange(null);
					this.userInput = null;
				}
			},

			handleStartInput(event) {
				if (this.userInput) {
					this.userInput = [event.target.value, this.userInput[1]];
				} else {
					this.userInput = [event.target.value, null];
				}
			},

			handleEndInput(event) {
				if (this.userInput) {
					this.userInput = [this.userInput[0], event.target.value];
				} else {
					this.userInput = [null, event.target.value];
				}
			},

			handleStartChange(event) {
				const value = this.parseString(this.userInput && this.userInput[0]);
				if (value) {
					this.userInput = [this.formatToString(value), this.displayValue[1]];
					const newValue = [value, this.picker.value && this.picker.value[1]];
					this.picker.value = newValue;
					if (this.isValidValue(newValue)) {
						this.emitInput(newValue);
						this.userInput = null;
					}
				}
			},

			handleEndChange(event) {
				const value = this.parseString(this.userInput && this.userInput[1]);
				if (value) {
					this.userInput = [this.displayValue[0], this.formatToString(value)];
					const newValue = [this.picker.value && this.picker.value[0], value];
					this.picker.value = newValue;
					if (this.isValidValue(newValue)) {
						this.emitInput(newValue);
						this.userInput = null;
					}
				}
			},

			handleClickIcon(event) {
				if (this.readonly || this.pickerDisabled) return;
				if (this.showClose) {
					this.valueOnOpen = this.value;
					event.stopPropagation();
					this.emitInput(null);
					this.emitChange(null);
					this.showClose = false;
					if (this.picker && typeof this.picker.handleClear === "function") {
						this.picker.handleClear();
					}
				} else {
					this.pickerVisible = !this.pickerVisible;
				}
			},

			handleClose() {
				if (!this.pickerVisible) return;
				this.pickerVisible = false;

				if (this.type === "dates" || this.type === "years" || this.type === "months") {
					// restore to former value
					const oldValue =
						parseAsFormatAndType(
							this.valueOnOpen,
							this.valueFormat,
							this.type,
							this.rangeSeparator
						) || this.valueOnOpen;
					this.emitInput(oldValue);
				}
			},

			handleFieldReset(initialValue) {
				this.userInput = initialValue === "" ? null : initialValue;
			},

			handleFocus() {
				const type = this.type;

				if (HAVE_TRIGGER_TYPES.indexOf(type) !== -1 && !this.pickerVisible) {
					this.pickerVisible = true;
				}
				this.$emit("focus", this);
			},

			handleKeydown(event) {
				const keyCode = event.keyCode;

				// ESC
				if (keyCode === 27) {
					this.pickerVisible = false;
					event.stopPropagation();
					return;
				}

				// Tab
				if (keyCode === 9) {
					if (!this.ranged) {
						this.handleChange();
						this.pickerVisible = this.picker.visible = false;
						this.blur();
						event.stopPropagation();
					} else {
						// user may change focus between two input
						setTimeout(() => {
							if (this.refInput.indexOf(document.activeElement) === -1) {
								this.pickerVisible = false;
								this.blur();
								event.stopPropagation();
							}
						}, 0);
					}
					return;
				}

				// Enter
				if (keyCode === 13) {
					if (
						this.userInput === "" ||
						this.isValidValue(this.parseString(this.displayValue))
					) {
						this.handleChange();
						this.pickerVisible = this.picker.visible = false;
						this.blur();
					}
					event.stopPropagation();
					return;
				}

				// if user is typing, do not let picker handle key input
				if (this.userInput) {
					event.stopPropagation();
					return;
				}

				// delegate other keys to panel
				if (this.picker && this.picker.handleKeydown) {
					this.picker.handleKeydown(event);
				}
			},

			handleRangeClick() {
				const type = this.type;

				if (HAVE_TRIGGER_TYPES.indexOf(type) !== -1 && !this.pickerVisible) {
					this.pickerVisible = true;
				}
				this.$emit("focus", this);
			},

			hidePicker() {
				if (this.picker) {
					this.picker.resetView && this.picker.resetView();
					this.pickerVisible = this.picker.visible = false;
					this.destroyPopper();
				}
			},

			showPicker() {
				if (this.$isServer) return;
				if (!this.picker) {
					this.mountPicker();
				}
				this.pickerVisible = this.picker.visible = true;

				this.updatePopper();

				this.picker.value = this.parsedValue;
				this.picker.resetView && this.picker.resetView();

				this.$nextTick(() => {
					_.$val(this, "picker.adjustSpinners") && this.picker.adjustSpinners();
				});
			},

			mountPicker() {
				this.picker = new Vue(this.panel).$mount();
				this.picker.defaultValue = this.defaultValue;
				this.picker.defaultTime = this.defaultTime;
				this.picker.popperClass = this.popperClass;
				this.popperElm = this.picker.$el;
				this.picker.width = this.reference.getBoundingClientRect().width;
				this.picker.showTime = this.type === "datetime" || this.type === "datetimerange";
				this.picker.selectionMode = this.selectionMode;
				this.picker.unlinkPanels = this.unlinkPanels;
				this.picker.arrowControl = this.arrowControl || this.timeArrowControl || false;
				this.$watch("format", format => {
					this.picker.format = format;
				});

				const updateOptions = () => {
					const options = this.pickerOptions;

					if (options && options.selectableRange) {
						let ranges = options.selectableRange;
						const parser = TYPE_VALUE_RESOLVER_MAP.datetimerange.parser;
						const format = DEFAULT_FORMATS.timerange;

						ranges = Array.isArray(ranges) ? ranges : [ranges];
						this.picker.selectableRange = ranges.map(range =>
							parser(range, format, this.rangeSeparator)
						);
					}

					for (const option in options) {
						if (
							options.hasOwnProperty(option) &&
							// 忽略 time-picker 的该配置项
							option !== "selectableRange"
						) {
							this.picker[option] = options[option];
						}
					}

					// main format must prevail over undocumented pickerOptions.format
					if (this.format) {
						this.picker.format = this.format;
					}
				};
				updateOptions();
				this.unwatchPickerOptions = this.$watch("pickerOptions", () => updateOptions(), {
					deep: true
				});
				this.$el.appendChild(this.picker.$el);
				this.picker.resetView && this.picker.resetView();

				this.picker.$on("dodestroy", this.doDestroy);
				this.picker.$on("pick", (date = "", visible = false) => {
					this.userInput = null;
					this.pickerVisible = this.picker.visible = visible;
					this.emitInput(date);
					this.picker.resetView && this.picker.resetView();
				});

				this.picker.$on("select-range", (start, end, pos) => {
					if (this.refInput.length === 0) return;
					if (!pos || pos === "min") {
						this.refInput[0].setSelectionRange(start, end);
						this.refInput[0].focus();
					} else if (pos === "max") {
						this.refInput[1].setSelectionRange(start, end);
						this.refInput[1].focus();
					}
				});
			},

			unmountPicker() {
				if (this.picker) {
					this.picker.$destroy();
					this.picker.$off();
					if (typeof this.unwatchPickerOptions === "function") {
						this.unwatchPickerOptions();
					}
					this.picker.$el.parentNode.removeChild(this.picker.$el);
				}
			},

			emitChange(val) {
				// determine user real change only
				if (!valueEquals(val, this.valueOnOpen)) {
					this.$emit("change", val);
					this.valueOnOpen = val;
					if (this.validateEvent) {
						this.dispatch("ElFormItem", "el.form.change", val);
					}
				}
			},

			emitInput(val) {
				const formatted = this.formatToValue(val);
				if (!valueEquals(this.value, formatted)) {
					this.$emit("input", formatted);
				}
			},

			isValidValue(value) {
				if (!this.picker) {
					this.mountPicker();
				}
				if (this.picker.isValidValue) {
					return value && this.picker.isValidValue(value);
				} else {
					return true;
				}
			}
		}
	});
}
</script>
<style lang="less">
.el-time-range-picker {
	width: 354px;
	overflow: visible;
}

.el-time-range-picker__content {
	position: relative;
	text-align: center;
	padding: 10px;
}

.el-time-range-picker__cell {
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
	margin: 0;
	padding: 4px 7px 7px;
	width: 50%;
	display: inline-block;
}

.el-time-range-picker__header {
	margin-bottom: 5px;
	text-align: center;
	font-size: 14px;
}

.el-time-range-picker__body {
	border-radius: var(--border-radius--mini);
	border: 1px solid #e4e7ed;
}

.el-time-panel {
	margin: 5px 0;
	border: 1px solid #e4e7ed;
	background-color: #fff;
	box-shadow: var(--normal-box-shadow);
	border-radius: var(--border-radius--mini);
	position: absolute;
	width: 180px;
	left: 0;
	z-index: 1000;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	-webkit-box-sizing: content-box;
	box-sizing: content-box;
}

.el-time-panel__content {
	font-size: 0;
	position: relative;
	overflow: hidden;
}

.el-time-panel__content::after,
.el-time-panel__content::before {
	content: "";
	top: 50%;
	position: absolute;
	margin-top: -15px;
	height: var(--ui-height);
	z-index: -1;
	left: 0;
	right: 0;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
	padding-top: 6px;
	text-align: left;
	border-top: 1px solid #e4e7ed;
	border-bottom: 1px solid #e4e7ed;
}

.el-time-panel__content::after {
	left: 50%;
	margin-left: 12%;
	margin-right: 12%;
}

.el-time-panel__content::before {
	padding-left: 50%;
	margin-right: 12%;
	margin-left: 12%;
}

.el-time-panel__content.has-seconds::after {
	left: calc(100% / 3 * 2);
}

.el-time-panel__content.has-seconds::before {
	padding-left: calc(100% / 3);
}

.el-time-panel__footer {
	border-top: 1px solid #e4e4e4;
	padding: 4px;
	height: 36px;
	line-height: 25px;
	text-align: right;
	-webkit-box-sizing: border-box;
	box-sizing: border-box;
}

.el-time-panel__btn {
	border: none;
	line-height: 28px;
	padding: 0 5px;
	margin: 0 5px;
	cursor: pointer;
	background-color: transparent;
	outline: 0;
	font-size: 12px;
	color: var(--el-text-color-primary);
}

.el-time-panel__btn.confirm {
	font-weight: 800;
	color: var(--el-color-primary);
}
</style>
